#include "actor.hpp"
#include <cassert>
#include <stdexcept>
#include "vm.hpp"
#include "assert_test.hpp"

using namespace std;

namespace BMD
{
  const int Actor::NO_LABEL = -1;

  /*************************************
   * function_info constructor
   ************************************/
  function_info::function_info() : addr(Actor::NO_LABEL), num_args(0) 
  { 
  }
  
  function_info::function_info(int _addr, uint _num_args) : 
    addr(_addr), num_args(_num_args) 
  { 
  }


  /*************************************
   * Actor methods
   ************************************/  
  //! construction
  Actor::Actor(unsigned int num_fields) : 
    should_throw(false), vm(new VM(this))
  {
    if (!vm) throw runtime_error("Could not alloacte virtual machine");
    fields.resize(num_fields);
  }
  
  Actor::Actor(string filename, unsigned int num_fields) : 
    should_throw(false), vm(new VM(this))
  {
    if (!vm) throw runtime_error("Could not alloacte virtual machine");
    fields.resize(num_fields);
    load_from_file(filename);
  }

  /** 
   * @param use_functions should be true iff function names should be
   * searched for as well
   */
  int Actor::get_label_address(string label, bool use_functions)
  {
    LabelMap::iterator addr = label_map.find(label);
    if (addr != label_map.end())
      return addr->second;
    
    if (use_functions)
      return get_function(label).addr;      
    else
      return Actor::NO_LABEL;
  }

  //! clears all data loaded from file
  void Actor::clear()
  {
    instructions.clear();
    label_map.clear();
    function_map.clear();
    fields.clear();
    assertions.clear();
    init_func = "";

    should_throw = false;
  }
  
  //! validate any assertions held in the assertions map
  void Actor::validate_assertions()
  {
    // grab all the assertions at the current point
    int op = vm->get_instruction_pointer();
    AssertionMap::iterator beg = assertions.lower_bound(op),
      end = assertions.upper_bound(op);
    
    // run them all
    for (AssertionMap::iterator it = beg; it != end; ++it)
      (*it).second->validate(vm);
  }

  /**
   * returns an function_info structure with
   * NO_LABEL as the address if the function is not found
   */
  function_info Actor::get_function(string name)
  {
    FunctionMap::iterator func = function_map.find(name);

    return (func == function_map.end()) ? function_info() : func->second;
  }

  InstructionRef Actor::get_instruction(int addr)
  {
    return instructions.at(addr);
  }

  void Actor::add_instruction(InstructionRef in)
  {
    instructions.push_back(in);
  }
 
  void Actor::add_label(string& str, int addr)
  {
    label_map.insert( make_pair(str, addr) );
  }

  /**** vm calls ****/
  void Actor::start()
  {
    vm->start();
  }

  /** run the program under normal conditions **/
  void Actor::run()
  {
    vm->run(); 
  }
  
  /** run the program, stopping at any assertions the should be tested **/
  void Actor::run_with_assertions()
  {
    start();
    while (vm->is_valid_ip() && vm->is_running())
    {
      vm->run(VM::STOP_AT_ASSERTIONS);
      validate_assertions();
    }
  }

  void Actor::set_debug_output(bool debug)
  {
    vm->set_debug_output(debug);
  }
  bool Actor::is_running() const
  {
    return vm->is_running();
  }
  Value Actor::call_function(string func_name, const vector<Value>& args, bool debug)
  {
    return vm->call_function(func_name, args, debug);
  }
  Actor::~Actor()
  {
    delete vm;
  }
}
